﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dapper;
using ProjetoSolo.QueroGas.Dominio.Entidades;
using ProjetoSolo.QueroGas.Dominio.Repositorios;
using ProjetoSolo.QueroGas.Infraestrutura.Validacao;

namespace ProjetoSolo.QueroGas.Infraestrutura.PersistenciaAdo.Repositorios
{
    public class RepositorioEmpresa : IRepositorioEmpresa
    {
        private readonly ContextoAdo contexto;

        public RepositorioEmpresa(IContextoRepositorio contexto)
        {
            this.contexto = (ContextoAdo)contexto;
        }

        public Empresa PegarPorCodigo(int codigo)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Empresa> PegarListaCompleta()
        {
            throw new NotImplementedException();
        }
       
        public int Cadastrar(Empresa empresa)
        {
            empresa.Validar();

            int codigoEmpresa = 0;

            IRepositorioEndereco repositorioEndereco = new RepositorioEndereco(this.contexto);
            int codigoEndereco = repositorioEndereco.Cadastrar(empresa.Endereco);

            codigoEmpresa = this.contexto.Conexao.Query<int>("insert into Empresa (Nome, Ativo, Endereco_IdEndereco, Usuario_IdUsuario) output inserted.IdEmpresa values(@Nome, @Ativa, @CodigoEndereco, @IdUsuario)",
                new { empresa.Nome, empresa.Ativa, codigoEndereco, empresa.Usuario.IdUsuario}, this.contexto.Transacao()).First();

            this.AdicionarTelefones(empresa.Telefones, codigoEmpresa);
            this.AdicionarProdutos(empresa.Produtos, codigoEmpresa);
            this.AdicionarDistribuidoras(empresa.Distribuidoras, codigoEmpresa);

            return codigoEmpresa;
        }

        public void Ativar(int codigo)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Empresa> BuscarPorEndereco(Endereco endereco)
        {
            RegraValidacao.VerificarObjetoNulo(endereco, "O endereço da empresa deve ser informado");

            StringBuilder query = new StringBuilder();
            query.Append("select Empresa.IdEmpresa, Empresa.Nome, Empresa.Ativo from Empresa ");
            query.Append("inner join Endereco on Endereco.IdEndereco = Empresa.Endereco_IdEndereco ");
            query.Append("where Endereco.cep = @Cep and Endereco.Estado = @Estado and Endereco.Bairro = @Bairro and ");
            query.Append("Endereco.Cidade = @Cidade and Endereco.Logradouro = @Logradouro and Endereco.Complemento = @Complemento");

            return this.contexto.Conexao.Query<Empresa>(query.ToString(), endereco, this.contexto.Transacao());
        }       

        private void AdicionarTelefones(IEnumerable<Telefone> telefones, int codigoEmpresa)
        {
            IRepositorioTelefone repositorio = new RepositorioTelefone(this.contexto);

            foreach (Telefone telefone in telefones)
            {
                int codigoTelefone = repositorio.Cadastrar(telefone);

                if (this.TelefoneEmpresaJahExiste(codigoEmpresa, codigoTelefone)) 
                    continue;
                
                if (codigoTelefone == 0)
                    codigoTelefone = repositorio.PegarCodigoTelefone(telefone);

                this.contexto.Conexao.Execute("insert into EmpresaTelefone values(@codigoEmpresa, @codigoTelefone)", new
                    {codigoEmpresa, codigoTelefone}, this.contexto.Transacao());
            }
        }

        private void AdicionarProdutos(IEnumerable<Produto> produtos, int codigoEmpresa)
        {
            foreach (Produto produto in produtos)
            {
                if (this.ProdutoEmpresaJahExiste(codigoEmpresa, produto.IdProduto))
                    continue;

                this.contexto.Conexao.Execute("insert into EmpresaProduto values(@preco, @codigoEmpresa, @IdProduto)", 
                    new { produto.Preco, codigoEmpresa, produto.IdProduto }, this.contexto.Transacao());
            }
        }

        private void AdicionarDistribuidoras(IEnumerable<Distribuidora> distribuidoras, int codigoEmpresa)
        {
            foreach (Distribuidora distribuidora in distribuidoras)
            {                
                if (this.DistribuidoraEmpresaJahExiste(codigoEmpresa, distribuidora.IdDistribuidora))
                    continue;

                this.contexto.Conexao.Execute("insert into EmpresaDistribuidora values(@codigoEmpresa, @IdDistribuidora)",
                    new { codigoEmpresa, distribuidora.IdDistribuidora }, this.contexto.Transacao());
            }
        }

        private bool TelefoneEmpresaJahExiste(int codigoEmpresa, int codigoTelefone)
        {
            StringBuilder query = new StringBuilder();
            query.Append("select IdEmpresaTelefone from EmpresaTelefone ");
            query.Append("where Empresa_IdEmpresa = @codigoEmpresa and Telefone_IdTelefone = @codigoTelefone ");
            
            return this.contexto.Conexao.Query<int>(query.ToString(), new { codigoEmpresa, codigoTelefone }, this.contexto.Transacao()).Any();
        }

        private bool ProdutoEmpresaJahExiste(int codigoEmpresa, int codigoProduto)
        {
            StringBuilder query = new StringBuilder();
            query.Append("select IdEmpresaProduto from EmpresaProduto ");
            query.Append("where Empresa_IdEmpresa = @codigoEmpresa and Produto_IdProduto = @codigoProduto ");

            return this.contexto.Conexao.Query<int>(query.ToString(), new { codigoEmpresa, codigoProduto }, this.contexto.Transacao()).Any();
        }

        private bool DistribuidoraEmpresaJahExiste(int codigoEmpresa, int codigoDistribuidora)
        {
            StringBuilder query = new StringBuilder();
            query.Append("select IdEmpresaDistribuidora from EmpresaDistribuidora ");
            query.Append("where Empresa_IdEmpresa = @codigoEmpresa and Distribuidora_IdDistribuidora = @codigoDistribuidora ");

            return this.contexto.Conexao.Query<int>(query.ToString(), new { codigoEmpresa, codigoDistribuidora }, this.contexto.Transacao()).Any();
        }
    }
}
